home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / mm / ccmd / cmfil.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-12-18  |  29.5 KB  |  1,085 lines

  1. /*
  2.  Copyright (c) 1986, 1990 by The Trustees of Columbia University in
  3.  the City of New York.  Permission is granted to any individual or
  4.  institution to use, copy, or redistribute this software so long as it
  5.  is not sold for profit, provided this copyright notice is retained.
  6.  
  7.  Author: Howie Kaye
  8. */
  9. /*
  10.  * Code to parse filenames.  Parsing succeeds if current input contains a 
  11.  * delimeter, and and the input up to the delimeter is nonempty, and contains
  12.  * either a uniquely matched filename, or an exactly matched wild or regexp 
  13.  * file spec.  In both of these cases, any other restrictions placed upon the 
  14.  * file (ie it's protection) must be met.  If a new file is being parsed, than
  15.  * either an existing, or a nonexisting filename will parse.
  16.  * Completion succeeds if the string passed to it is an unambiguous match 
  17.  * for a file name, or if it is a valid wildcard filespec.  If a new file is 
  18.  * being parse, no match is necessary.
  19.  * Standard help displays up to `cmcsb._cmmax' filenames in a columnal
  20.  * fashion.  If more filenames than this would be listed, then
  21.  * the number of possible completions is listed instead.
  22.  * The standard break table allows letters, digits, dots, dashes, underscores,
  23.  * tildes, and '#'; wild characters: '*', '?', meta characters: '[', ']',
  24.  * '{', '}'.
  25.  */
  26.                     /* allocate file errors here */
  27. #define FILERR                /* include _CMFIL error symbols */
  28.  
  29. #include "ccmdlib.h"
  30. #include "cmfncs.h"
  31. #include "cmfil.h"
  32. #include "filelist.h"
  33.  
  34. #ifndef NGROUPS
  35. #define NGROUPS 1
  36. #endif
  37.  
  38. #if unix
  39. /*
  40.  * break table for filenames under Unix
  41.  */
  42.                     /* standard break table */
  43. static brktab filbrk = {        /* all valid chars for filenames */
  44.   {                    /* alphanums, "~#/_-\[]+" */
  45.     0xff, 0xff, 0xff, 0xff, 0xa1, 0x00, 0x00, 0x0b, 
  46.     0x00, 0x00, 0x00, 0x08, 0x80, 0x00, 0x00, 0x09, 
  47.   },
  48.   {
  49.     0xff, 0xff, 0xff, 0xff, 0xa1, 0x00, 0x00, 0x0b, 
  50.     0x00, 0x00, 0x00, 0x08, 0x80, 0x00, 0x00, 0x09, 
  51.   }
  52. };
  53. #else
  54. #if MSDOS
  55. /*
  56.  * break table for filenames under MSDOS
  57.  */
  58.                     /* standard break table */
  59. static brktab filbrk = {        /* all valid chars for filenames */
  60.   {                    /* alphanums, "~", "#", "/" */
  61.     0xff,  0xff,  0xff,  0xff,  0xf7,  0xc8,  0x00,  0x3f,  
  62.     0x80,  0x00,  0x00,  0x09,  0x80,  0x00,  0x00,  0x1d,  
  63.   },
  64.   {
  65.     0xff,  0xff,  0xff,  0xff,  0xf7,  0xc8,  0x00,  0x3f,  
  66.     0x80,  0x00,  0x00,  0x09,  0x80,  0x00,  0x00,  0x1d,  
  67.   }
  68. };
  69.                     /* wild card break table */
  70. static brktab filwldtab = {        /* all valid chars for filenames */
  71.   {                    /* alphanums, "~", "#", "/" */
  72.     0xff,  0xff,  0xff,  0xff,  0xfb,  0xdc,  0x00,  0x3f,  
  73.     0x80,  0x00,  0x00,  0x17,  0x80,  0x00,  0x00,  0x1f,  
  74.   },
  75.   {
  76.     0xff,  0xff,  0xff,  0xff,  0xfb,  0xdc,  0x00,  0x3f,  
  77.     0x80,  0x00,  0x00,  0x17,  0x80,  0x00,  0x00,  0x1f,  
  78.   }
  79. };
  80.                     /* regex break tab */
  81. static brktab filregtab = {
  82.   {
  83.     0xff,  0xff,  0xff,  0xff,  0xf7,  0xfc,  0x00,  0x3f,  
  84.     0x80,  0x00,  0x00,  0x05,  0x80,  0x00,  0x00,  0x1f  
  85.   },
  86.   {
  87.     0xff,  0xff,  0xff,  0xff,  0xf7,  0xcc,  0x00,  0x3e,  
  88.     0x80,  0x00,  0x00,  0x01,  0x80,  0x00,  0x00,  0x1f  
  89.   },
  90. };
  91. #endif /* MSDOS */
  92. #endif /* !unix */
  93.  
  94. #ifndef MAXPATHLEN
  95. #define MAXPATHLEN 256
  96. #endif
  97.  
  98. #define FNAME(x) (&namebuf[(x)->offset])
  99.  
  100. int fil_parse(), fil_help(), fil_complete();
  101. pvfil buildexactfilelist(),buildpartialfilelist();
  102. char *malloc(),*partial(),*tilde_expand(), *default_ext();
  103.                     /* standard break mask */
  104. ftspec ft_fil =  { fil_parse, fil_help, fil_complete, 0, &filbrk };
  105.  
  106. extern filecount;
  107. extern char *namebuf;
  108. static char *dotpath[] = {".", NULL};
  109. static filblk deffilblk = {        /* default fileblock */
  110.   dotpath,                /* search path */
  111.   NULL,                    /* exceptionspec */
  112.   NULL,                    /* extension */
  113. };
  114.  
  115.  
  116. /*
  117.  * parse routine
  118.  * parse succeeds if:
  119.  * non-wild parse, and one terminated match, or
  120.  * wild parse, and 1 or more matches, or
  121.  * new file parse and 0 or more matches.
  122.  */
  123. PASSEDSTATIC int
  124. fil_parse(text, textlen, fdbp, parselen, value)
  125. char *text;
  126. int textlen, *parselen;
  127. fdb *fdbp;
  128. pval *value;
  129. {
  130.   static char retbuf[200];
  131.   static char *ret[2] = { retbuf, NULL };
  132.   int fcount,ecount,pcount,imatch;    /* files, exact, partial,
  133.                        inv matches */
  134.   dirfile **filelist;            /* list of files */
  135.   char *term;                /* termination character */
  136.   int flags;
  137.   filblk *fblk;
  138.  
  139.   if ((fdbp->_cmffl & FIL_WLD) && (fdbp->_cmffl & FIL_PO))
  140.     return(FILxINV);
  141.  
  142.   flags = fdbp->_cmffl;            /* allow directories too */
  143.   if (fdbp->_cmffl & FIL_EXEC)
  144.     fdbp->_cmffl |= FIL_ASRCH;
  145.                     /* find the number of matches */
  146.   if (textlen == 0)            /* nothing typed */
  147.       return(CMxINC);            /*  so don't do lookup */
  148.  
  149.   fcount = fil_match(text, textlen, fdbp, &filelist, &term, &pcount, &ecount,             &imatch);
  150.  
  151.   fdbp->_cmffl = flags;            /* restore real flags */
  152.  
  153.  
  154.   if (term == NULL) {            /* unterminated. */
  155.     if (ecount == 0 && pcount == 0 && !(flags & FIL_PO))
  156.       return(FILxNM);
  157.     return(CMxINC);            /* pass incompleteness up */
  158.   }
  159.  
  160.   if (ecount == 0) {            /* no matches */
  161.     if (fdbp->_cmffl & FIL_PO) {    /* if new file parse then succeed */
  162.       value->_pvfil = ret;
  163.       if (text[0] == '~') {
  164.       char buf[200]; 
  165.       strncpy(buf, text, term-text);
  166.       buf[term-text]= '\0';
  167.       strcpy(value->_pvfil[0], tilde_expand(buf));
  168.       }
  169.       else {
  170.       strncpy(value->_pvfil[0],text,term-text);
  171.       value->_pvfil[0][term-text] = '\0';
  172.       }
  173.       *parselen = term-text;
  174.       if (validate(value->_pvfil[0], fdbp->_cmffl)) /* or fail, if invalid /*
  175.                             /*   filename */
  176.     return(CMxOK);
  177.       else return(FILxBAD);
  178.     }
  179.     if (pcount > 0) {            /* check again with def extension */
  180.       fblk = (filblk *) fdbp->_cmdat;
  181.       if (fblk == NULL) fblk = &deffilblk;
  182.       if (fblk->def_extension) {
  183.     int i;
  184.     static char buf[MAXPATHLEN];
  185.     for ( i= 0; i < textlen; i++) {
  186.       if (isspace(text[i]&0x7f)) break;
  187.       buf[i] = text[i]&0x7f;
  188.     }
  189.     buf[i] = '\0';
  190.     strcat(buf,default_ext(buf,"",fblk->def_extension,TRUE,filelist,
  191.           fcount));
  192.     *parselen = term - text;
  193.     fcount = fil_match(buf, strlen(buf), fdbp, &filelist, &term, &pcount,
  194.                        &ecount, &imatch);
  195.     if (ecount >= 1) {
  196.       if (iswild(buf))
  197.         return(FILxAMB);
  198.       value->_pvfil = buildexactfilelist(filelist,fcount,1);
  199.       return(CMxOK);
  200.     }
  201.     else return(FILxNM);
  202.       }
  203.       else {
  204.       if (flags & FIL_DIR) {    /* looking for directory names? */
  205.           int i;
  206.           char buf[256];
  207.           /*
  208.            * If input ends in / or consists of ~username, return
  209.            * the directory name
  210.            */
  211.           buf[i = term - text] = 0;
  212.           while (i--)        /* XXX need to strip 8th bit? */
  213.           buf[i] = text[i] & 0x7f;
  214.           i = term - text - 1;    /* look at last character */
  215.           if ((buf[i] == '/') ||
  216.           (buf[0] == '~' && index (buf, '/') == 0)) {
  217.           value->_pvfil = ret;
  218.           strcpy (retbuf, tilde_expand (buf));
  219.           *parselen = term - text;
  220.           return (CMxOK);
  221.           }
  222.       }
  223.       value->_pvfil = buildpartialfilelist(filelist,fcount,pcount);
  224.       return(FILxPMA);        /* return partial match */
  225.       }
  226.     }    
  227.     else return(FILxNM);        /* otherwise, no match */
  228.   }
  229.   else                    /* exact match? */
  230.     if (ecount == 1 || ecount >= 1 && fdbp->_cmffl & FIL_WLD)
  231.       goto success;
  232.   else {                /* multiple matches */
  233.     if (fdbp->_cmffl & FIL_WLD) {    /* wild? */
  234.       value->_pvfil = buildexactfilelist(filelist,fcount,ecount);
  235.       *parselen = term - text;        /* find the length */
  236.       return(CMxOK);
  237.     }
  238.     else {
  239.       static char tbuf[MAXPATHLEN];
  240.       strncpy(tbuf,text,textlen); tbuf[textlen] = '\0';
  241.       if (!iswild(tbuf)) {        /* take first name */
  242.     value->_pvfil = buildexactfilelist(filelist,fcount,1);
  243.     *parselen = term - text;
  244.     return(CMxOK);
  245.       }
  246.       else return(FILxAMB);        /* otherwise ambiguous */
  247.     }
  248.   }
  249. success:                /* a sucessful parse */
  250.   *parselen = term - text;        /* find the length */
  251.   value->_pvfil = buildexactfilelist(filelist,fcount,ecount);
  252.   return(CMxOK);            /* return success */
  253. }
  254.  
  255.  
  256. /*
  257.  * complete:
  258.  * file name completion routine.
  259.  */
  260. PASSEDSTATIC int
  261. fil_complete(text, textlen, fdbp, full, cplt, cpltlen) 
  262. char *text,**cplt;
  263. int textlen,full,*cpltlen;
  264. fdb *fdbp;
  265. {
  266.   int fcount,ecount,pcount,icount;
  267.   dirfile **filelist;
  268.   char *term;
  269.   int flags;
  270.   int i;
  271.   int first;
  272.   int exact;
  273.                     /* find matches */
  274.   *cplt = "";
  275.   *cpltlen = 0;
  276.   flags = fdbp->_cmffl;
  277.   if (fdbp->_cmffl & FIL_EXEC)
  278.     fdbp->_cmffl |= FIL_ASRCH;
  279.  
  280.   fcount = fil_match(text, textlen, fdbp, &filelist, &term, &pcount, &ecount,
  281.              &icount);
  282.   fdbp->_cmffl = flags;
  283.   pcount -= icount;
  284.   ecount -= icount;
  285.   if (ecount >= 1) {            /* exact match.  use it */
  286.     *cpltlen = 0;            /* 0 length completion */
  287.     *cplt = "";                /* point at nothing */
  288.     goto done;                /* and return */
  289.   }
  290.   if (pcount == 0) {            /* no matches. */
  291.     if (textlen > 0) 
  292.       if (fdbp->_cmffl & FIL_PO) {    /* a new file? */
  293.     filblk *fblk;
  294.     fblk = (filblk *) fdbp->_cmdat;
  295.     if (fblk == NULL) fblk = &deffilblk;
  296.  
  297.     if (fblk->def_extension) {
  298.       static char completion[MAXPATHLEN];
  299.       static char tbuf[MAXPATHLEN];
  300.       strncpy(tbuf,text,textlen);
  301.       tbuf[textlen] = '\0';
  302.       strcpy(completion,*cplt);
  303.       strcat(completion,default_ext(tbuf,*cplt,fblk->def_extension,
  304.         TRUE,filelist,fcount));
  305.       if (strcmp(*cplt,completion) || fdbp->_cmffl & FIL_PO) {
  306.         *cplt = completion;
  307.         *cpltlen = strlen(*cplt);
  308.         goto done;
  309.       }
  310.       return(CMP_BEL);
  311.     }
  312.     else {
  313.       *cplt = "";            /* accept unmatching text */
  314.       *cpltlen = 0;
  315.       goto done;
  316.     }
  317.       }
  318.                     /* otherwise */
  319.     *cplt = NULL;            /* return null pointer */
  320.     *cpltlen = 0;
  321.     return(CMP_BEL);            /* take action. */
  322.   }
  323.  
  324.                     /* one or more match */
  325.   *cplt = partial(text,textlen,filelist,fcount,pcount,&exact);
  326.   *cpltlen = strlen(*cplt);
  327.   if (!exact) {
  328.     filblk *fblk;
  329.     fblk = (filblk *) fdbp->_cmdat;
  330.     if (fblk == NULL) fblk = &deffilblk;
  331.  
  332.     if (fblk->def_extension) {
  333.       static char completion[MAXPATHLEN];
  334.       static char tbuf[MAXPATHLEN];
  335.       strncpy(tbuf,text,textlen);
  336.       tbuf[textlen] = '\0';
  337.       strcpy(completion,*cplt);
  338.       strcat(completion,default_ext(tbuf,*cplt,fblk->def_extension,
  339.                          fdbp->_cmffl & FIL_PO,filelist,fcount));
  340.       if (strcmp(*cplt,completion) || (fdbp->_cmffl & FIL_PO)) {
  341.     *cplt = completion;
  342.     *cpltlen = strlen(*cplt);
  343.     goto done;
  344.       }
  345.       return(CMP_BEL);
  346.     }
  347.     return(CMP_BEL);
  348.   }
  349. done:
  350.   if (full) {
  351.     if (fdbp->_cmffl & FIL_NODIR) {
  352.       char *cp;
  353.       int isdir;
  354.       cp = (char *)calloc(textlen+strlen(*cplt)+1, 1);
  355.       strncpy(cp,text,textlen);
  356.       strcat(cp,*cplt);
  357.       isdir = do_stat(tilde_expand(cp)) & FIL_ADR;
  358.       free(cp);
  359.       if (isdir) {
  360.         strcat(*cplt, "/");
  361.     (*cpltlen)++;
  362.         return(CMP_BEL);
  363.       }
  364.     }
  365.     return(CMP_GO | CMP_SPC);
  366.   }
  367.   else return(CMP_PNC);
  368. }
  369.  
  370. /*
  371.  * help routine
  372.  */
  373. PASSEDSTATIC int
  374. fil_help(text, textlen, fdbp, cust, lines) 
  375. char *text;
  376. int textlen, cust;
  377. fdb *fdbp;
  378. int lines;
  379. {
  380.   int fcount,pcount,ecount,icount;    /* number of files/matches */
  381.   dirfile **d,**filelist;        /* list of them */
  382.   char *term;
  383.   int found,i,j,maxlen,fillen;
  384.   int cols,curcol,putlen;
  385.   char *realdir;
  386.   filblk *fblk;
  387.   int mylines = 0;
  388.  
  389.   if (!cust) {                /* standard help msg */
  390.     cmxputs("filename, one of the following:"); 
  391.   }
  392.   cmxflsh();                /* fil_match may be slow...  */
  393.   fcount = fil_match(text, textlen, fdbp, &filelist, &term, &pcount, &ecount,
  394.              &icount);
  395.   pcount -= icount;
  396.   ecount -= icount;
  397.   if (pcount == 0) {
  398.     cmxnl();
  399.     cmxputs(" (No filenames match current input)"); /* none here */
  400.     return(lines -1);            /* all done */
  401.   }
  402.   fblk = (filblk *) fdbp->_cmdat;
  403.   if (fblk == NULL) fblk = &deffilblk;
  404.   if (pcount > cmcsb._cmmax) {
  405.     static char buf[MAXPATHLEN];
  406.     sprintf(buf,"  (%d matching filenames)",pcount,cmcsb._cmmax);
  407.     cmxnl();
  408.     cmxputs(buf);
  409.     return(CMxOK);
  410.   }
  411.  
  412.   maxlen = 0;
  413.   found = 0;
  414.   for (i = 0; i < fcount; i++) {
  415.     if ((filelist[i]->flags & (FIL_MAT|FIL_NOR|FIL_INV)) == FIL_MAT) {
  416.       if (fdbp->_cmffl & FIL_NOEXT) 
  417.     fillen = fnamlen(FNAME(filelist[i]));
  418.       else
  419.     fillen = strlen(FNAME(filelist[i]));
  420.       if (!(fdbp->_cmffl & FIL_NOPTH)) {
  421.     fillen += strlen(tilde_expand(filelist[i]->directory));
  422.     if (!index(DIRSEP,
  423.           filelist[i]->directory[strlen(filelist[i]->directory)-1]))
  424.       fillen+=1;
  425.       }
  426.       if (fdbp->_cmffl & FIL_TYPE) fillen += 1;
  427.       if (maxlen < fillen) maxlen = fillen;
  428.       if (++found >= pcount) break;
  429.     }
  430.   }
  431.   maxlen += 3;
  432.   /* XXX see comments in cmkey.c on the subject of multi-column output */
  433.   cols = (cmcsb._cmcmx+2) / maxlen;    /* number of columns per line */
  434.   if (cols <= 0) cols = 1;
  435.   curcol = 0;                /* currently printing first column */
  436.   for (i = 0, found = 0; i < fcount && found < pcount; i++) {
  437.     if ((filelist[i]->flags & (FIL_MAT|FIL_NOR|FIL_INV)) == FIL_MAT) {
  438.       found++;
  439.       putlen = 0;
  440.       if (curcol == 0) {
  441.     mylines++;
  442.         cmxnl();            /* new line for first column */
  443.     if (mylines >= lines) {
  444.       if (!cmhelp_more("--space to continue, Q to stop--"))
  445.           return(-1);
  446.       else {
  447.           lines = cmcsb._cmrmx;
  448.           mylines = 0;
  449.       }
  450.     }
  451.         cmxputs(" ");            /* and offset a bit */
  452.       }
  453.       if (!(fdbp->_cmffl & FIL_NOPTH)) {
  454.         if (strcmp(filelist[i]->directory,".")) {
  455.       realdir = tilde_expand(filelist[i]->directory);
  456.         cmxputs(realdir);
  457.       if (!index(DIRSEP,realdir[strlen(realdir)-1])) {
  458.         cmxputc('/');
  459.         putlen++;
  460.       }
  461.           putlen+= strlen(realdir);
  462.         }
  463.     else realdir = filelist[i]->directory;
  464.       }
  465.       else realdir = "";
  466.       if (!(fdbp->_cmffl & FIL_NOEXT)) {
  467.         cmxputs(FNAME(filelist[i]));
  468.         putlen += strlen(FNAME(filelist[i]));
  469.       }
  470.       else {
  471.         int l = fnamlen(FNAME(filelist[i]));
  472.         int k;
  473.         for (k = 0; k < l; k++) cmxputc(FNAME(filelist[i])[k]);
  474.         putlen += l;
  475.       }
  476.      if (fdbp->_cmffl & FIL_TYPE) {
  477.     if (!(filelist[i]->flags & FIL_STAT)) {
  478.       static char fname[MAXPATHLEN];
  479.       strcpy(fname,realdir);
  480.       if (strlen(realdir) != 0)
  481.         if (fname[strlen(fname)-1] != '/')
  482.           strcat(fname,"/");
  483.       strcat(fname,FNAME(filelist[i]));
  484.       filelist[i]->flags |= do_stat(tilde_expand(fname));
  485.     }
  486.     if (filelist[i]->flags & FIL_ADR)
  487.       cmxputc('/');
  488.     else if (filelist[i]->flags & FIL_ALK)
  489.       cmxputc('@');
  490.     else if (filelist[i]->flags & FIL_AEX)
  491.       cmxputc('*');
  492.     else cmxputc(' ');
  493.     putlen++;
  494.       }
  495.       if (curcol < (cols-1)) {        /* space out if not last column */
  496.     int j;
  497.         for (j = putlen; j < maxlen; j++)
  498.       cmxputc(SPACE);
  499.       }
  500.       curcol = (curcol+1) % cols;    /* and move to next column */
  501.     }
  502.   }
  503.   /* cmxnl(); */
  504.   return(lines - mylines);            /* all done */
  505. }
  506.  
  507. PASSEDSTATIC int
  508. fil_match(text, textlen, fdbp, mat, term,pmatch,ematch,imatch) 
  509. char *text,**term;            /* termination character */
  510. int textlen;                /* length of text */
  511. fdb *fdbp;                /* pointer to fdb */
  512. dirfile ***mat;                /* list of matching files */
  513. int *pmatch,*ematch,*imatch;        /* partial, and exact match counts */
  514. {
  515.   static char dirname[MAXPATHLEN], fname[MAXPATHLEN];
  516.   int i,j;
  517.   char **path, *rindex();
  518.   char *xpath[2];
  519.   char *suffix = NULL;
  520.   filblk *fblk;
  521.   int fcount;
  522.   static char nambuf[MAXPATHLEN];
  523.   int namlen;
  524.   dirfile *df,**search_path(),**m, **fake_search_path();
  525.   int access_flags,access;
  526.   int e,p,toklen;
  527.   brktab *btab;                /* break table to use */
  528.   int nosearch = FALSE;
  529.   char f[BUFSIZ];
  530.   struct stat sbuf;
  531.  
  532.  
  533.   if ((btab = fdbp->_cmbrk) == NULL)    /* get supplied break table */
  534.     btab = &filbrk;            /* or use default */
  535.  
  536.   xpath[0] = dirname;
  537.   xpath[1] = NULL;
  538.  
  539.   fblk = (filblk *) fdbp->_cmdat;
  540.   if (fblk == NULL) fblk = &deffilblk;
  541.  
  542.  
  543.   dirname[0] = fname[0] = '\0';        /* initially no name */
  544.  
  545.   for( i= 0; i < textlen; i++) 
  546.     if (BREAK(btab,text[i],i))
  547.       break;
  548.   toklen = i;
  549.  
  550.   for(i = toklen-1; i >= 0; i--) {    /* find final part of name */
  551.     if (index(DIRSEP,text[i])) break;
  552.   }
  553.  
  554.   path = fblk->pathv;            /* so use default path passed us */
  555.   if (path == NULL) {            /* and if none, use "." */
  556.     path = dotpath;      
  557.   }
  558.  
  559.   if (i >= 0) {                /* found a directory  */
  560.     for(j = 0; j < i; j++)        /* separate out directory */
  561.       dirname[j] = text[j] & 0x7f;    /* and convert to 7 bit */
  562. #ifdef undef
  563.     if (j == 0) strcpy(dirname,"/");    /* check for just a "/" */
  564.     else dirname[j] = '\0';        /* null terminate */
  565. #else
  566.     dirname[j] = '\0';
  567. #endif
  568.     if (index(STRUCTTERM,text[i])) 
  569.       strcat(dirname,":");
  570.     if (index(STRUCTTERM,text[i-1]))
  571.       strcat(dirname,"/");
  572.     suffix = xpath[0];            /* and use this as the search path */
  573.   }
  574.  
  575.   else if ((text[0] & 0x7f) == '~') {    /* no directory, but a ~username */
  576.       char tmpbuf[30];            /* so make ~/.. the directory */
  577.       char *cp;                /* and basename(~) the filename */
  578.       
  579.       strncpy(tmpbuf, text, textlen);
  580.       tmpbuf[textlen] = '\0';
  581.  
  582.       strcpy(dirname, tilde_expand(tmpbuf));
  583.       if (strcmp(dirname,tmpbuf) == 0) { /* couldn't expand... */
  584.       dirname[0] = '\0';        /* leave as filename */
  585.       }
  586.       else {
  587.       char *cp;
  588.  
  589.       if (dirname[strlen(dirname)] == '/' && strlen(dirname) != 1)
  590.           dirname[strlen(dirname)] = '\0'; /* remove trailing '/' */
  591.       cp = rindex(dirname, '/'); /* find last part of name */
  592.       if (cp) {
  593.           *cp++ = '\0';
  594.           strcpy(fname, cp);
  595.           nosearch = TRUE;
  596.           m = fake_search_path(dirname, fname, &fcount);
  597.           *term = &text[textlen];
  598.       }
  599.       else {            /* no directory in home dir??? */
  600.           strcpy(fname, dirname);
  601.           dirname[0] = '\0';
  602.           nosearch = TRUE;
  603.           m = fake_search_path(".", fname, &fcount);
  604.           *term = &text[textlen];
  605.       }
  606.       }
  607.   }
  608.  
  609.   if (!nosearch) {
  610.       *term = NULL;            /* and find termination char */
  611.       for(j = i+1; j < textlen; j++)    /* separate out the filename */
  612.       if (!isspace(text[j]&0x7f))
  613.           fname[j-(i+1)] = text[j] & 0x7f; /* and convert to 7 bit */
  614.       else {
  615.           *term = &text[j];
  616.           break;
  617.       }
  618.       fname[j-(i+1)] = '\0';        /* and null terminate */
  619.       m = search_path(path,suffix,&fcount); /* search the directories */
  620.       if (fcount == 0) {        /* none found */
  621.                     /* see if unsearchable path */
  622.                     /* and fake the entry to be an */ 
  623.                     /* exact match */
  624.       sprintf(f,"%s/%s", tilde_expand(dirname), fname);
  625.       if (stat(f,&sbuf) == 0) {
  626.           m = fake_search_path(tilde_expand(dirname),fname,&fcount);
  627.       }
  628.       }
  629.   }
  630.  
  631.  
  632.   
  633.   *pmatch = 0;                /* no matches so far */
  634.   *ematch = 0;
  635.   *imatch = 0;
  636.  
  637.   access_flags = fdbp->_cmffl & FIL_ALL;/* extract file access codes */
  638.  
  639.   for (i = 0; i < fcount; i++) {
  640.     e = p = 0;
  641.     df = m[i];                /* current file */
  642.     df->flags &= ~(FIL_MAT|FIL_EXA|FIL_INV|FIL_NOR);
  643.     strcpy(nambuf,tilde_expand(df->directory));
  644.     if (nambuf[strlen(nambuf)-1] != '/')
  645.       strcat(nambuf,"/");
  646.     namlen = strlen(nambuf);
  647.  
  648.     if (fmatch(FNAME(df),fname,TRUE)) {    /* partially matching? */
  649.       df->flags |= FIL_MAT;        /* mark it matching */
  650.       p = 1;                /* flag a match */
  651.     }
  652.  
  653.     if (fmatch(FNAME(df),fname,FALSE)) { /* exact matching? */
  654.       df->flags |= FIL_EXA;        /* yup...mark it */
  655.       e = 1;                /* and flag it */
  656.     }
  657.  
  658.     if (access_flags == 0) {
  659.       if (e) (*ematch)++;
  660.       if (p) {
  661.     (*pmatch)++;
  662.     if (fblk->exceptionspec != NULL) /* if in exception list */
  663.       if(fmatch(FNAME(df),fblk->exceptionspec,FALSE)) {
  664.         df->flags |= FIL_INV;        /* then ignore it */
  665.         (*imatch)++;
  666.       }
  667.       }
  668.       continue;
  669.     }
  670.     else if (e || p) {            /* if match, stat it */
  671.       if (!(df->flags & FIL_STAT)) {    /* was it stat'ed yet? */
  672.     strcpy(&nambuf[namlen],FNAME(df)); /* complete the name */
  673.     df->flags |= do_stat(tilde_expand(nambuf));
  674.       }
  675.       access = FALSE;            /* check access flags */
  676.       if (access_flags & FIL_EXEC && df->flags & FIL_AEX)
  677.     access = TRUE;
  678.       else
  679.       if (access_flags & FIL_RD && df->flags & FIL_ARD)
  680.     access = TRUE;
  681.       else
  682.       if (access_flags & FIL_WR && df->flags & FIL_AWR)
  683.     access = TRUE;
  684.       else
  685. #ifdef MSDOS
  686.       if (access_flags & FIL_HID && df->flags & FIL_AHD)
  687.     access = TRUE;
  688.       else
  689.       if (access_flags & FIL_SYS && df->flags & FIL_ASY)
  690.     access = TRUE;
  691.       else
  692. #endif /* MSDOS */
  693.       if (access_flags & FIL_EXEC && df->flags & FIL_AEX)
  694.     access = TRUE;
  695.       else
  696.       if (access_flags & FIL_DIR && df->flags & FIL_ADR)
  697.     access = TRUE;
  698.       if (access) {
  699.     if (e) (*ematch)++;
  700.     if (p) {            /* a match */
  701.       (*pmatch)++;            /* count it */
  702.       if (fblk->exceptionspec != NULL) /* if in exception list */
  703.         if(fmatch(FNAME(df),fblk->exceptionspec,FALSE)) {
  704.           df->flags |= FIL_INV;    /* make it invisible */
  705.           (*imatch)++;
  706.         }
  707.     }
  708.       }
  709.       else df->flags |= FIL_NOR;    /* ignore it */
  710.     }
  711.   }
  712.   *mat = m;
  713.   return(fcount);
  714. }
  715.  
  716. /*
  717.  * build a list of all matching, unignored files.
  718.  * matchtype can be either exact or partial
  719.  */
  720.  
  721. PASSEDSTATIC pvfil
  722. buildfilelist(fl,count,matches,matchtype) dirfile **fl; int count,matches; {
  723.   static char **retv=NULL;
  724.   static int length=0;
  725.   int i,j;
  726.   static char dir[MAXPATHLEN];
  727.  
  728.   if (retv != NULL) {            /* free up previous values */
  729.     for(i = 0; i < length; i++) free(retv[i]);
  730.     free(retv);
  731.   }
  732.   length = matches;
  733.  
  734.                     /* build list */
  735.   retv = (char **) malloc(sizeof(char *) * (matches + 1));
  736.                     /* find matches */
  737.   for(i = 0,j = 0; i < count && j < matches; i++) {
  738.     if ((fl[i]->flags & (matchtype|FIL_NOR)) == matchtype) {
  739.       int len;
  740.       strcpy(dir,tilde_expand(fl[i]->directory));
  741.       if (index(DIRSEP,dir[strlen(dir)-1])) { /* if it has a '/' */
  742.      retv[j] = malloc(sizeof(char)*(strlen(FNAME(fl[i]))+strlen(dir)+1));
  743.     strcpy(retv[j],dir);        /* copy directory */
  744.     strcat(retv[j],FNAME(fl[i]));    /* and filename */
  745.       }
  746.       else 
  747.     if (strcmp(dir,"."))  {        /* no slash.   copy name and insert */
  748.       retv[j] = malloc(sizeof(char)*(strlen(FNAME(fl[i]))+strlen(dir)+2));
  749.       strcpy(retv[j],dir);
  750.       strcat(retv[j],"/");
  751.       strcat(retv[j],FNAME(fl[i]));
  752.     }
  753.       else {                /* in "." don't use directory */
  754.     retv[j] = malloc((strlen(FNAME(fl[i])) + 1) * sizeof(char *));
  755.     strcpy(retv[j],FNAME(fl[i]));
  756.       }
  757.       j++;                /* for all files */
  758.     }
  759.   }
  760.   retv[matches] = NULL;            /* null terminate vector */
  761.   return(retv);
  762. }
  763.  
  764. /*
  765.  * build list of partially matching files
  766.  */
  767. PASSEDSTATIC pvfil
  768. buildpartialfilelist(fl,count,matches)
  769. dirfile **fl; int count,matches; 
  770. {
  771.   return(buildfilelist(fl,count,matches,FIL_MAT));
  772. }
  773.  
  774. /*
  775.  * build list of exactly matching files
  776.  */
  777. PASSEDSTATIC pvfil
  778. buildexactfilelist(fl,count,matches)
  779. dirfile **fl; int count,matches; 
  780. {
  781.   return(buildfilelist(fl,count,matches,FIL_EXA));
  782. }
  783.  
  784. /*
  785.  * get length of filename up to the extension
  786.  * skip leading dots
  787.  */
  788. PASSEDSTATIC int
  789. fnamlen(nam) char *nam; {
  790.   register int i;
  791.   for (i = 0; i < strlen(nam); i++)    /* skip leading dot's */
  792.     if (nam[i] != '.') break;
  793.   for (i = 1; i < strlen(nam); i++)    /* stop at final dot */
  794.     if (nam[i] == '.') break;
  795.   return(i);
  796. }
  797.  
  798.  
  799. /*
  800.  * find a partial completion for a list of files
  801.  */
  802.  
  803. PASSEDSTATIC char *
  804. partial(text,textlen,filelist,fcount,pcount,exact) 
  805. char *text; 
  806. int textlen;
  807. dirfile **filelist;
  808. int fcount,pcount;
  809. int *exact;
  810. {
  811.   int i,j,k;
  812.   static char buf[MAXPATHLEN];
  813.   static char tbuf[MAXPATHLEN],fbuf[MAXPATHLEN],fname[MAXPATHLEN];
  814.   int buflen,fbuflen;
  815.  
  816.   *exact = TRUE;            /* assume we find an exact match */
  817.   for(i = textlen-1; i >= 0; i--)    /* get the name */
  818.     if (index(DIRSEP,text[i])) break;
  819.  
  820.   for(j = i+1; j < textlen; j++)
  821.     tbuf[j-(i+1)] = text[j] & 0x7f;    /* copy text */
  822.   tbuf[j-(i+1)] = '\0';            /* null terminate it */
  823.        
  824.   buf[0] = '\0';            /* start off with no matches */
  825.   for(i = 0, j = 0; i < fcount && j < pcount; i++) {
  826.     if ((filelist[i]->flags & (FIL_MAT|FIL_INV|FIL_NOR)) == FIL_MAT) {
  827.       strcpy(fbuf,FNAME(filelist[i]));
  828.       fbuflen = strlen(fbuf);        /* copy then name */
  829.       while(!fmatch(fbuf,tbuf,FALSE)) {    /* shorten until we match */
  830.     fbuf[--fbuflen] = '\0';
  831.       }
  832.       strcpy(fname,FNAME(filelist[i]));
  833.       if (j++ == 0)
  834.     strcpy(buf,&fname[fbuflen]);    /* first time, grab completion */
  835.       else {
  836.     buflen = strlen(buf);
  837.     for(k = 0; k < buflen; k++)    /* otherwise trim it to match */
  838.       if (buf[k] != fname[fbuflen+k]) {
  839.         buf[k] = '\0';        /* if end of a name, then exact */
  840.         if (fname[fbuflen+k] != '\0') *exact = FALSE;
  841.         else *exact = TRUE;
  842.         break;
  843.       }
  844.       }
  845.     }
  846.   }
  847.   return(buf);
  848. }
  849.  
  850. /*
  851.  * based on current type in, return part of default extension to 
  852.  * append to filename
  853.  */
  854.  
  855. PASSEDSTATIC char *
  856. default_ext(fname, cplt, ext, new,filelist,fcount)
  857. char *fname,*cplt,**ext;
  858. int new;
  859. dirfile **filelist;
  860. int fcount;
  861. {
  862.   int i,j,k,l;
  863.   int start;
  864.   static char buf[MAXPATHLEN],*e;
  865.   if (ext == NULL) return ("");
  866.   strcpy(buf,fname);            /* get name */
  867.   strcat(buf,cplt);            /* append completion */
  868.   for(start = 0; start < strlen(buf); i++) /* skip leading dots */
  869.     if (buf[start] != '.') break;
  870.   for(i = strlen(buf)-1; i >= start; i--) /* look for existing extension */
  871.     if (buf[i] == '.') break;
  872.   if (i <= start) i = strlen(buf);    /* none.  point to end of string */
  873.   if (!new)                /* if existing file  */
  874.     if (buf[i] == '\0') return("");    /* and no "." inserted, all done */
  875.   for(e=ext[0],k = 0; ext[k] != NULL; k++,e = ext[k]) {    /* for all extensions*/
  876.     for(j = i; j < strlen(buf); j++) {    /* check for a match */
  877.       if (buf[j] != e[j-i]) {        /* no match.  try next extension */
  878.     break;
  879.       }
  880.     }
  881.     if (j == strlen(buf)) {        /* success... */
  882.       static char tbuf[MAXPATHLEN];    /* see how much of the extension */
  883.       strcpy(tbuf,buf);            /* we need to use */
  884.       strcat(tbuf,&e[j-i]);        /* cuz some may be typed already */
  885.       for (l = 0; l < fcount; l++)
  886.     if (filelist[l]->flags & FIL_MAT) 
  887.       if (fmatch(FNAME(filelist[l]),tbuf,FALSE)) 
  888.         return(&e[j-i]);
  889.     }
  890.   }
  891.   if (ext[k] == NULL) {            /* no matches.  if new ok */
  892.     if (new) {
  893.       for(start = 0; start < strlen(buf); i++)
  894.     if (buf[start] != '.') break;
  895.       for(i = strlen(buf)-1; i >= start; i--) /* use first extension */
  896.     if (buf[i] == '.') break;    /* find the . */
  897.       if (i <= start) i = strlen(buf);    /* no dot.  goto end */
  898.       for(j = i; j < strlen(buf); j++) { /* check for a match */
  899.     if (buf[j] != ext[0][j-i]) {    /* no match.  */
  900.       return("");            /* fail */
  901.     }
  902.       }
  903.       return(&ext[0][j-i]);        /* return extension */
  904.     }
  905.     else return("");            /* nothing to do */
  906.   }
  907. }
  908.  
  909. /*
  910.  * validate a filename
  911.  * for now, in MSDOS, just check the length
  912.  */
  913. PASSEDSTATIC 
  914. validate(fname, flags) 
  915. char *fname; 
  916. int flags;
  917. {
  918.  
  919. #ifdef unix
  920.   struct stat sbuf;
  921.   char c, *cp, *rindex();
  922.  
  923.   if (fname == NULL || *fname == '\0')
  924.     return (FALSE);
  925.   if (!(flags & FIL_VAL))        /* no validation requested */
  926.     return (TRUE);            /* anything goes */
  927.  
  928.   if (stat (fname, &sbuf) == 0)        /* good enough. we can stat the file */
  929.     return (TRUE);
  930.  
  931.   /* XXX think of DIRSEP instead of '/' */
  932.   if ((cp = rindex (fname, '/')) == NULL) /* no directory name */
  933.     return (TRUE);
  934.   else {
  935.     c = *cp;
  936.     *cp = '\0';
  937.     if (stat (fname, &sbuf) == 0) {
  938.       *cp = c;
  939.       return (TRUE);
  940.     }
  941.     else {
  942.       *cp = c;
  943.       return (FALSE);
  944.     }
  945.   }
  946. #endif
  947. #ifdef MSDOS
  948. /*
  949.  * for now, just check by length
  950.  */
  951.  
  952.   int i,j;
  953.   for(i = 0; i < strlen(fname); i++) 
  954.     if (fname[i] == '.') break;
  955.   if (i > 8) return(FALSE);
  956.   for(j = i+1; j < strlen(fname); j++);
  957.     if (j - i > 4) return(FALSE);
  958.   return(TRUE);
  959. #endif
  960. }
  961.  
  962.  
  963. /*
  964.  * get info on a file.
  965.  * need to know if it is a link, a directory, and the user's access to it
  966.  */
  967. PASSEDSTATIC 
  968. do_stat(name)
  969. char *name;
  970. {
  971.   struct stat statbuf;
  972.   int flags=0;
  973. #if unix
  974.   int i;
  975.   int uid,mgroups,gid[NGROUPS];        /* hold our groups */
  976.   int ngroups = NGROUPS;
  977.   int done = FALSE;
  978.  
  979. #ifdef S_IFLNK
  980.   if (!lstat(name,&statbuf)) {        /* do a stat */
  981.     flags |= FIL_STAT;            /* mark it stat()'ed */
  982.     if ((statbuf.st_mode & S_IFLNK) == S_IFLNK) {
  983.       flags |= FIL_ALK;            /* if a link, note it */
  984.       stat(name,&statbuf);        /* and get the real info */
  985.     }
  986. #else
  987.   if (!stat(name,&statbuf)) {
  988.     flags |= FIL_STAT;
  989. #endif /* S_IFLNK */
  990.  
  991. /*
  992.  * check file protection.  check world first.  then owner,
  993.  * then group
  994.  */
  995.     if(statbuf.st_mode & S_IFDIR)
  996.     flags |= FIL_ADR;
  997.     uid = geteuid();            /* get effective uid */
  998.     if (uid == 0) {            /* root? */
  999.     flags |= FIL_ARD | FIL_AWR;    /* automatic read/write access */
  1000.     if (statbuf.st_mode & 0111)    /* and execute access if anyone does */
  1001.         flags |= FIL_AEX;
  1002.     done = TRUE;
  1003.     }
  1004.     else if (uid == statbuf.st_uid) {    /* am i the owner? */
  1005.     if (statbuf.st_mode & S_IREAD)    /* owner read? */
  1006.         flags |= FIL_ARD;
  1007.     if (statbuf.st_mode & S_IWRITE)    /* owner write? */
  1008.         flags |= FIL_AWR;
  1009.     if (statbuf.st_mode & S_IEXEC)    /* owner execute? */
  1010.         flags |= FIL_AEX;
  1011.     done = TRUE;
  1012.     }
  1013.     if (!done) {            /* still don't know.  check group */
  1014. #if NGROUPS > 1
  1015.     getgroups(ngroups,gid);
  1016. #else
  1017.     gid[0] = getgid();
  1018.     ngroups = 1;
  1019. #endif
  1020.     for(i= 0; i < ngroups; i++) 
  1021.       if (gid[i] == statbuf.st_gid) {
  1022.         if (statbuf.st_mode & 040)    /* group read? */
  1023.           flags |= FIL_ARD;
  1024.         if (statbuf.st_mode & 020)    /* group write? */
  1025.           flags |= FIL_AWR;
  1026.         if (statbuf.st_mode & 010)    /* group execute? */
  1027.           flags |= FIL_AEX;
  1028.         done = TRUE;
  1029.         break;
  1030.       }
  1031.     }
  1032.     if (!done) {
  1033.     if (statbuf.st_mode & 4)        /* world read? */
  1034.         flags |= FIL_ARD;
  1035.     if (statbuf.st_mode & 2)        /* world write? */
  1036.         flags |= FIL_AWR;
  1037.     if (statbuf.st_mode & 1)        /* world execute? */
  1038.         flags |= FIL_AEX;
  1039.     }
  1040.     
  1041.   }
  1042. #endif /* unix */
  1043. #ifdef MSDOS
  1044.   if (!stat(name,&statbuf)) {
  1045.     flags = FIL_STAT;
  1046.     if (statbuf.st_mode & S_IFDIR)
  1047.       flags |= FIL_ADR;
  1048.     if (statbuf.st_mode & S_IREAD)
  1049.       flags |= FIL_ARD;
  1050.     if (statbuf.st_mode & S_IEXEC)
  1051.       flags |= FIL_AEX;
  1052.     if (statbuf.st_mode & S_IWRITE)
  1053.       flags |= FIL_AWR;
  1054.   }
  1055. #endif /* MSDOS */
  1056.                     /* if dir and execute */
  1057.   if (flags & (FIL_ADR|FIL_AEX) == (FIL_ADR|FIL_AEX)) {
  1058.     flags &= ~FIL_AEX;
  1059.     flags |= FIL_ASRCH;            /* then turn on search bit instead */
  1060.   }
  1061.   return(flags);
  1062. }
  1063.  
  1064.  
  1065. /*
  1066.  * fake a dirfile list of length one, pointing at dir and filename.
  1067.  */
  1068. dirfile **
  1069. fake_search_path(dir, fname, count)
  1070. char *dir, *fname;
  1071. int *count;
  1072. {
  1073.     static dirfile df;
  1074.     static dirfile *dfp = &df;
  1075.     static char nbuf[BUFSIZ];
  1076.     
  1077.     *count = 1;
  1078.     strcpy(nbuf, fname);
  1079.     namebuf = nbuf;
  1080.     dfp->directory = dir;
  1081.     dfp->offset = 0;
  1082.     dfp->flags = 0;
  1083.     return(&dfp);
  1084. }
  1085.